﻿using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Threading;
using System.Windows.Threading;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Command;
using LifeV2.Model;

namespace LifeV2.ViewModel
{
    /// <summary>
    ///   This class contains properties that the main View can data bind to.
    ///   <para>
    ///     Use the <strong>mvvminpc</strong> snippet to add bindable properties to this ViewModel.
    ///   </para>
    ///   <para>
    ///     You can also use Blend to data bind with the tool's support.
    ///   </para>
    ///   <para>
    ///     See http://www.galasoft.ch/mvvm/getstarted
    ///   </para>
    /// </summary>
    public class MainViewModel : ViewModelBase
    {
        /// <summary>
        ///   The <see cref = "World" /> property's name.
        /// </summary>
        public const string WorldPropertyName = "World";

        /// <summary>
        ///   The <see cref = "SelectedLifeType" /> property's name.
        /// </summary>
        public const string SelectedTypePropertyName = "SelectedLifeType";

        /// <summary>
        ///   The <see cref = "IsRunning" /> property's name.
        /// </summary>
        public const string IsRunningPropertyName = "IsRunning";

        private World _world;
        private bool isRunning;
        private Type[] lifeFormTypes;
        private Type selectedLifeType;
        private BackgroundWorker updateThread = new BackgroundWorker();
        private DispatcherTimer timer = new DispatcherTimer();
        private TimeSpan interval = new TimeSpan(0, 0, 0, 0, 1);

        /// <summary>
        ///   Initializes a new instance of the MainViewModel class.
        /// </summary>
        public MainViewModel()
        {
            // Code runs in Blend --> create design time data.
            int width = 50, height = 20;
            World = new World(width, height);

            World.LifeMatrix = new ObservableCollection<ILifeForm>(World.LifeMatrix);

            if (IsInDesignMode)
            {
            }
            else
            {
                // Code runs "for real"

                StartCommand = new RelayCommand(Start, () => !timer.IsEnabled);
                StopCommand = new RelayCommand(Stop, () => timer.IsEnabled);
                ResetCommand = new RelayCommand(Reset, () => !timer.IsEnabled);

                updateThread.DoWork += UpdateWorld;
                timer.Tick += (sender, args) => updateThread.RunWorkerAsync();
            }
        }

        /// <summary>
        ///   Gets the IsRunning property.
        ///   TODO Update documentation:
        ///   Changes to that property's value raise the PropertyChanged event. 
        ///   This property's value is broadcasted by the Messenger's default instance when it changes.
        /// </summary>
        public bool IsRunning
        {
            get { return isRunning; }

            set
            {
                if (isRunning == value)
                {
                    return;
                }

                bool oldValue = isRunning;
                isRunning = value;

                // Update bindings, no broadcast
                RaisePropertyChanged(IsRunningPropertyName);
            }
        }

        /// <summary>
        ///   Gets the World property.
        ///   Changes to that property's value raise the PropertyChanged event. 
        ///   This property's value is broadcasted by the Messenger's default instance when it changes.
        /// </summary>
        public World World
        {
            get { return _world; }

            set
            {
                if (_world == value)
                {
                    return;
                }

                _world = value;

                // Update bindings, no broadcast
                RaisePropertyChanged(WorldPropertyName);
            }
        }

        public Type[] LifeFormTypes
        {
            get
            {
                if (lifeFormTypes == null)
                {
                    lifeFormTypes = Assembly.GetAssembly(typeof(ILifeForm)).GetTypes().Where(t => typeof(ILifeForm).IsAssignableFrom(t) && !t.IsInterface).ToArray();
                }
                return lifeFormTypes;
            }
        }

        /// <summary>
        ///   Gets the SelectedLifeType property.
        ///   TODO Update documentation:
        ///   Changes to that property's value raise the PropertyChanged event. 
        ///   This property's value is broadcasted by the Messenger's default instance when it changes.
        /// </summary>
        public Type SelectedLifeType
        {
            get { return selectedLifeType; }
            set
            {
                if (selectedLifeType == value)
                {
                    return;
                }

                Type oldValue = selectedLifeType;
                selectedLifeType = value;

                // Update bindings, no broadcast
                RaisePropertyChanged(SelectedTypePropertyName);
            }
        }

        public RelayCommand StartCommand { get; set; }

        public RelayCommand StopCommand { get; set; }

        public RelayCommand ResetCommand { get; set; }

        private void Reset()
        {
        }

        private void Stop()
        {
            timer.Stop();
        }

        private void Start()
        {
            timer.Start();
        }

        private void UpdateWorld(object sender, EventArgs e)
        {
            var r = new Random();
            for (int i = 0; i < World.Width; i++)
            {
                for (int j = 0; j < World.Height; j++)
                {
                    var creature = new Creature(World, i, j)
                                       {
                                           Health = r.Next(0, 100)
                                       };

                    World[i, j] = creature;
                }
            }
        }

        ////public override void Cleanup()
        ////{
        ////    // Clean up if needed

        ////    base.Cleanup();
        ////}
    }
}